import useUser from '@/store/user'
import { sendMessage, uploadMsgFile } from '@/assets/api/message'
import useChannel from '@/store/channel'
import { v4 } from 'uuid'
import { ElMessageBox } from 'element-plus'
import EmojiData from '@/components/emoji/emoji-data'
import dayjs from 'dayjs'
import useApp from '@/store/app.js'

/**
 * @typedef parseResult
 * @property {boolean} has 是否有提及
 * @property {string} [prev] 提及之前的字符串
 * @property {string} next 提及之后的字符串
 * @property {string} target 目标字符串
 * @property {string} [name] 目标后的关键字
 */
/**
 * @typedef parseOptions
 * @property {boolean} reverse 倒序
 * @property {number} cursorStart 光标位置
 */

/**
 * 检查字符串中是否有 @ 某人
 * @param {string} str
 * @param {parseOptions} options
 * @return {parseResult}
 */
export function parseMention(str, options = {}) {
  const opt = { reverse: false, cursorStart: str.length }
  Object.assign(opt, options)
  let reg = /@[^@\s]+\s/
  const string = toValue(str).slice(0, opt.cursorStart)
  if (opt.reverse) {
    reg = /@[^@\s]+\s(?!@)/
  }
  const found = string.match(reg)
  const prev = found ? string.slice(0, found.index) : ''
  const next = found ? string.slice(found.index + found[0].length) : ''
  const target = found ? found[0].slice(0, -1) : ''
  const word = target ? target.replace('@', '') : ''
  return {
    has: found !== null,
    prev,
    next,
    target,
    word,
  }
}

export function getMentionList(str) {
  return [...str.matchAll(/@[^@\s]+\s/g)].map((i) => i[0].slice(1).slice(0, -1))
}

export function parseEmoji(str) {
  const reg = /\[(.+?)]/
  const string = toValue(str)
  const found = string.match(reg)
  const prev = found ? string.slice(0, found.index) : ''
  const next = found ? string.slice(found.index + found[0].length) : ''
  const target = found ? handleEmoji(found[1]) : ''
  return {
    has: Boolean(target.emoji),
    prev,
    next,
    target,
  }
}
/**
 * 根据 emoji 名称处理 emoji表情
 * @param emojiName
 * @returns {{emoji: *, name: *, type: (*|string)}}
 */
export function handleEmoji(emojiName) {
  const targetList = emojiName.split('_')
  const type = targetList.length > 1 ? targetList[0] : 'default'
  const name = targetList.length > 1 ? targetList[1] : targetList[0]
  const emoji = EmojiData[`${type}Emoji`]?.data.find((i) => i.name === name)
  return {
    type,
    name,
    emoji,
  }
}
// 数组对象去重
export const uniqueBy = (arr, key) => {
  const seen = new Set()
  return arr.filter((item) => {
    const keyValue = item[key]
    return seen.has(keyValue) ? false : seen.add(keyValue)
  })
}

/**
 * @typedef {Object} TextHandleOptions
 * @property {Boolean} [shieldMention] 屏蔽提及,即在文字中不再显示提及
 * @property {Boolean} [links] 链接列表
 * @property {Function | null} [isMentionUser] 是否提及当前用户回调处理
 * @property {Function | null} [emojiText] 是否以文本形式显示 emoji
 */
/**
 * 处理文本
 * @param {String} str
 * @param {Number} line
 * @param {TextHandleOptions} options
 * @return {string}
 */
export function textHandle(str, line = 0, options = {}) {
  const opt = {
    shieldMention: false,
    showLink: false,
    links: [],
    mentionCallback: null,
    emojiText: false,
  }
  Object.assign(opt, options)
  if (!str) return ''
  let html = ''
  // 将<全部替换成&lt;,防止html标签被解析成html
  str = str.replaceAll('<', '&lt;')
  if (opt.showLink) {
    opt.links?.forEach((link) => {
      str = str.replaceAll(link.url, `<a href="${link.url}" target="_blank">${link.url}</a>`)
    })
    // 替换完清空 links
    opt.links = []
  }
  const oldStr = str
  // 查看是否有@
  const mentionResult = parseMention(str)
  if (mentionResult.has) {
    html += mentionResult.prev
    if (!opt.shieldMention) {
      // 将空格放到 @人员 之后,注意闭合标签后的空格要和文本域保持一致
      // const s = `<span class="post-text mention">${mentionResult.target}</span> `
      const tool = createTextTool({
        className: 'post-text mention',
        content: `${mentionResult.target} `,
      })
      html += opt.mentionCallback ? opt.mentionCallback(tool) || tool.getValue() : tool.getValue()
    }
    str = mentionResult.next
  }
  // 查询是否有表情
  const emojiResult = parseEmoji(str)
  if (emojiResult.has) {
    html += emojiResult.prev
    const { emoji, type, name } = emojiResult.target
    if (opt.emojiText) {
      html += `[${name}]`
    } else {
      html += `<img class="post-text ${type === 'default' ? 'emoji' : `${type}-emoji`}" src="${
        emoji.url
      }" alt="[${name}]">`
    }
    str = emojiResult.next
  }
  return html + (oldStr === str ? str : textHandle(str, line, opt))
}

function createTextTool({ tagName = 'span', className, content }) {
  return {
    setTag: (name) => {
      tagName = name || tagName
    },
    setClass: (classes) => {
      className = classes || className
    },
    addClass: (classes) => {
      className = `${classes} ${className}`
    },
    setContent: (str) => {
      content = str || content
    },
    getContent: () => {
      return content
    },
    getValue: () => {
      return `<${tagName} class="${className}">${content}</${tagName}>`
    },
  }
}

/**
 * 重发消息
 * @param messageBox 消息
 */
export function reSend(messageBox) {
  return new Promise((resolve, reject) => {
    messageBox.error = false
    messageBox.loading = true
    if (messageBox.content || messageBox.clientFiles.length) {
      send(messageBox, messageBox.clientFiles, messageBox.rootId, resolve, reject)
    }
  })
}

/**
 * 发送消息
 * @param {Object} message 消息
 * @param {Blob[]} files 文件列表
 * @param {String} rootId 根消息id
 */
export function useSendMessage(message, files = [], rootId = null) {
  return new Promise((resolve, reject) => {
    const clientFiles = files.map((i) => {
      return reactive({
        uuid: v4(),
        name: i.name,
        size: i.size,
        progress: 0,
        file: i,
      })
    })
    if (message.content || clientFiles.length) {
      if (message.content) {
        const messageBox = createMessageBox(message, rootId)
        messageBox.content = message.content
        send(messageBox, [], rootId, resolve, reject)
      }
      if (clientFiles.length) {
        clientFiles.forEach((file) => {
          const messageBox = createMessageBox(message, rootId)
          messageBox.clientFiles = [file]
          send(messageBox, messageBox.clientFiles, rootId, resolve, reject)
        })
      }
    }
  })
}

function send(messageBox, clientFiles = [], rootId, resolve, reject) {
  // 2s 后显示加载状态
  const timeoutId = setTimeout(() => {
    !messageBox.success && (messageBox.loading = true)
  }, 2000)
  const channelStore = useChannel()
  channelStore.addChannelMsg(messageBox)
  sendMessage(messageBox)
    .then((res) => {
      if (res.code === 200) {
        resolve && resolve()
        messageBox.loading = false
        messageBox.success = true
        clearTimeout(timeoutId)
        clientFiles.forEach((i) => {
          const formData = new FormData()
          formData.set('channelId', channelStore.channelId)
          formData.set('messageId', res.data.id)
          formData.set('uuid', i.uuid)
          formData.set('file', i.file)
          uploadMsgFile(formData, (e) => {
            i.progress = e.progress
          })
        })
        channelStore.updateDialog(channelStore.channelId, 'lastMsgTime', res.data.createTime)
      } else {
        clearTimeout(timeoutId)
        messageBox.loading = false
        messageBox.error = true
        reject && reject()
      }
    })
    .catch((err) => {
      console.error(err)
      clearTimeout(timeoutId)
      messageBox.loading = false
      messageBox.error = true
      reject && reject()
    })
}

/**
 * 创建消息
 */
function createMessageBox(message, rootId) {
  const userStore = useUser()
  const channelStore = useChannel()
  const appStore = useApp()
  return reactive({
    id: null,
    uuid: v4(),
    channelId: channelStore.channelId,
    userId: userStore.userId,
    createUser: userStore.nickName,
    createTime: appStore.currentTime,
    rootId,
    mentionUserList: message.mentionUserList.map((i) => i.userId),
    originalId: null,
    content: '',
    type: 1,
    replyMsgId: message.replyMsgId || null,
    files: [],
    metaData: {
      msgForwardData: null,
      replyMsgData: null,
      availableReplyUsers: [],
      textTags: [],
      reactions: [],
      systemData: null,
      contentType: 'text',
      count: {
        replyCount: 0,
        unreadCount: channelStore.members.length - 1,
      },
      status: {
        readFlag: true,
        mentionMe: false,
        forwardFlag: false,
        tagFlag: false,
        editFlag: false,
        pinnedFlag: false,
        replyFlag: false,
        urgentFlag: false,
      },
    },
    props: {},
    priority: 0,
    loading: false,
    error: false,
  })
}

// 删除频道
export function removeChannel(channelId, options, callback) {
  const channelStore = useChannel()
  const {
    content = '',
    title = '',
    type = null,
    showCancelButton = false,
    showClose = false,
    closeOnClickModal = false,
    closeOnPressEscape = false,
    confirmButtonText = '我知道了',
  } = options
  return new Promise((resolve) => {
    ElMessageBox.confirm(content, title, {
      showCancelButton,
      showClose,
      type,
      closeOnClickModal,
      closeOnPressEscape,
      confirmButtonText,
    })
      .then(() => {
        if (callback) {
          callback()
        } else {
          resolve()
        }
        channelStore.removeChannel(channelId)
      })
      .catch(() => {
        resolve()
      })
  })
}

// 频道重定向
export function redirect() {
  const channelStore = useChannel()
  if (channelStore.channelList.length > 0) {
    channelStore.setDialog(channelStore.channelList[0])
    return `?channelId=${channelStore.channelList[0].id}`
  } else {
    channelStore.setDialog(null)
    return ''
  }
}

// 消息时间戳格式化
export function transformTimeStamp(time) {
  const { currentTime } = useApp()
  time = dayjs(time || currentTime)
  if (time.isToday()) {
    return `${time.format('H:mm')}`
  } else if (time.isYesterday()) {
    return `昨天 ${time.format('H:mm:ss')}`
  } else if (dayjs(currentTime).year() === time.year()) {
    return time.format('M-D')
  } else {
    return time.format('YYYY-M-D')
  }
}

/**
 * @typedef {Object} MessageToStringOptions
 * @property {String} mode 模式, 'string'|'html', 默认值 string
 */
/**
 * 消息处理为字符串
 * @param message
 * @param [options] {MessageToStringOptions}
 * @returns {string}
 */
export function handelMessageToString(message, options) {
  if (!message) return ''
  const opt = Object.assign(
    {
      mode: 'string',
    },
    options
  )
  const isHtml = opt.mode === 'html'
  const channelStore = useChannel()
  const { createBy, content, type, createUser, clientFiles, files, channelId, metaData, props } = message
  const { status, contentType, systemData } = metaData
  const userStore = useUser()
  const isCurrentUser = createBy === userStore.userId
  let result = ''
  if (type === 1) {
    const channelCount = channelStore.countStatisticsMap.get(channelId)
    if (channelCount && channelCount.mentionRootCount > 0) {
      const str = '[有人@你]'
      result += isHtml ? `<span style="color: var(--elp-color-danger);">${str}</span>` : str
    }
    result += isHtml ? `<span>${createUser}: </span>` : `${createUser}: `
    if ((clientFiles || files)?.length > 0) {
      const str = '[有文件]'
      result += isHtml ? `<span>${str}</span>` : str
    }
    if (status.forwardFlag) {
      const str = '[转发消息]'
      result += isHtml ? `<span>${str}</span>` : str
    }
    if (content) {
      result += isHtml ? `<span>${content}</span>` : content
    }
  } else if (type === 2) {
    const { messageType, deleteByUser, realName, memberList } = systemData
    if (messageType === 'recall_message') {
      const str = `${isCurrentUser ? '你' : deleteByUser} 撤回了一条消息`
      result += isHtml ? `<div>${str}</div>` : str
    } else if (messageType === 'leave_channel') {
      const str = `${realName} 退出了群聊`
      result += isHtml ? `<div>${str}</div>` : str
    } else if (messageType === 'add_member') {
      const str = `${realName} 邀请了 ${memberList.map((i) => i.realName).join('、')} 进入群聊`
      result += isHtml ? `<div>${str}</div>` : str
    } else if (messageType === 'remove_member') {
      const str = `${realName} 将 ${memberList.map((i) => i.realName).join('、')} 移出了群聊`
      result += isHtml ? `<div>${str}</div>` : str
    }
  }
  return result
}
